#include "TcpSocketOps.h"
#include <stdio.h> // for snprintf
#include "Log.h"

#define ___my_swab32(x) \
	((uint32_t)( \
			(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
			(((uint32_t)(x) & (uint32_t)0x0000ff00UL) <<  8) | \
			(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >>  8) | \
			(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) ))

namespace dsth {
namespace net {
    int CreateNonblockSocketOrDie() {
        int fd = static_cast<int>(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
        if (INVALID_SOCKET == fd) {            
            LOG_FATAL("create tcp socket error, errorcode:%d.", ::WSAGetLastError());
        }

        // set non-block
        unsigned long ul = 1;
        int ret = ioctlsocket(fd, FIONBIO, (unsigned long*)&ul);
        if (SOCKET_ERROR == ret) {
            LOG_FATAL("set non-block failed. errorcode:%d.", ::WSAGetLastError());
        }

        // set FIONREAD
        unsigned long num_to_read = 0;
        ret = ioctlsocket(fd, FIONBIO, &num_to_read);
        if (SOCKET_ERROR == ret) {
            LOG_FATAL("set FIONREAD failed. errorcode:%d.", ::WSAGetLastError());
        }
        return fd;
    }

    int BindOrDie(int fd, const struct sockaddr_in& addr) {
        int ret = ::bind(fd, (sockaddr*)&addr, sizeof addr);
        if (SOCKET_ERROR == ret) {
            LOG_FATAL("bind socket failed. errorcode:%d.", ::WSAGetLastError());
        }
        return ret;
    }

    int ListenOrDie(int fd) {
        int ret = ::listen(fd, SOMAXCONN);
        if (SOCKET_ERROR == ret) {
            LOG_FATAL("listen socket error. errorcode:%d.", ::WSAGetLastError());
        }
        return ret;
    }

    void Close(int fd) {
        int ret = ::closesocket(fd);
        if (SOCKET_ERROR == ret) {
            LOG_ERROR("close socket error. errorcode:%d.", ::WSAGetLastError());
        }
    }

    int Write(int fd, const char* data, size_t len) {
        return ::send(fd, data, static_cast<int>(len), 0);
    }

    int Read(int fd, char* buf, size_t len) {
        return ::recv(fd, buf, static_cast<int>(len), 0);
    }

    int Readv(int fd, WSABUF* buf, int buf_count, int* recv_bytes, int* flag) {
        return ::WSARecv(fd, buf, buf_count, (LPDWORD)recv_bytes, (LPDWORD)flag, NULL, NULL);
    }

    int Accept(int fd, struct sockaddr_in& addr) {
        int len = sizeof addr;
        int conn_fd = static_cast<int>(::accept(fd, (sockaddr*)&addr, &len));
        if (conn_fd == INVALID_SOCKET) {
            LOG_ERROR("socket accept failed, errorcode:%d.", ::WSAGetLastError());
            ::closesocket(conn_fd);
            return conn_fd;
        }

        // set non-block
        unsigned long ul = 1;
        int ret = ioctlsocket(conn_fd, FIONBIO, (unsigned long*)&ul);
        if (SOCKET_ERROR == ret) {
            LOG_ERROR("set conn_fd non-block failed. errorcode:%d.", ::WSAGetLastError());
        }
        return conn_fd;
    }

    int Connect(int fd, const struct sockaddr_in& addr) {
        return ::connect(fd, (SOCKADDR*)&addr, sizeof addr);
    }

    struct sockaddr_in GetHostAddr(int sockfd) {
        struct sockaddr_in host_addr;
        memset(&host_addr, 0x00, sizeof host_addr);
        int addr_len = sizeof(host_addr);
        if (::getsockname(sockfd, (sockaddr*)&host_addr, &addr_len) < 0) {
            LOG_ERROR("GetHostAddr | getsockname failed.");
        }
        return host_addr;
    }

    struct sockaddr_in GetPeerAddr(int sockfd) {
        struct sockaddr_in peer_addr;
        memset(&peer_addr, 0x00, sizeof peer_addr);
        int addr_len = sizeof(peer_addr);
        if (::getpeername(sockfd, (sockaddr*)&peer_addr, &addr_len) < 0) {
            LOG_ERROR("GetPeerAddr | getpeername failed.");
        }
        return peer_addr;
    }

    void ToHostPortFormat(char* buf, size_t buf_len, const struct sockaddr_in& addr) {
        char* host = inet_ntoa(addr.sin_addr);
        unsigned short port = htons(addr.sin_port);
        snprintf(buf, buf_len, "%s:%u", host, port);
    }

    int GetSockErr(int fd) {
        int error;
        int error_len = sizeof error;
        return ::getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &error_len);
    }

    int32_t N2H_32(int32_t be) {
        return ___my_swab32(be);
    }

    int32_t H2N_32(int32_t le) {
        return ___my_swab32(le);
    } 

    void SetReuse(bool enable, int fd) {
        BOOL opt = enable ? TRUE : FALSE;
        int ret = ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof opt);
        if (ret == SOCKET_ERROR) {
            LOG_ERROR("set reuseaddr failed. errorcode:%d.", ::WSAGetLastError());
        }
    }

    void ShutDownWrite(int fd) {
        if (::shutdown(fd, SD_SEND) == SOCKET_ERROR) {
            LOG_ERROR("shutdown failed. errorcode:%d.", ::WSAGetLastError());
        }
    }
    
}// net
}// dsth
